﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Tomahawk.Runtime.Utils;
using Microsoft.Xna.Framework.Graphics;
using Tomahawk.Serialization;
using System.Xml.Serialization;
using Tomahawk.Serialization.SerialBoost;

namespace Tomahawk.Runtime.Components
{
    public class ProfilerParameters : OverlayParameters
    {
        public struct CounterDescriptor
        {
            [XmlAttribute("Group")]
            public string Group;

            [XmlAttribute("Instance")]
            public string Instance;

            [XmlAttribute("Name")]
            public string Name;
        }

        [XmlAttribute("MemoryStats")]
        public bool MemoryStats = true;

        [XmlAttribute("Performance")]
        public bool Performance = true;

        [XmlAttribute("Profiling")]
        public bool Profiling = true;

        [XmlAttribute("ProfilingStats")]
        public bool ProfilingStats = true;

        [XmlAttribute("ProfilingHistogram")]
        public bool ProfilingHistogram = false;

        [XmlAttribute("ProfilingFrequency")]
        public float ProfilingFrequency = 0.2f;

        [XmlElement("PerformanceCounters")]
        public SerializableList<CounterDescriptor> PerformanceCounters = new SerializableList<CounterDescriptor>();
    }

    public class ProfilerComponent : OverlayComponent
    {
        #region Instance
        private static ProfilerComponent instance = null;
        public static ProfilerComponent Instance { get { return instance; } }
        #endregion

        #region Properties
        protected float ResetTime = 5;
        protected TimeSpan elapsedTime = TimeSpan.Zero;
        protected bool MemoryStats = true;
        protected bool Performance = true;
        protected bool Profiling = true;
        protected bool ProfilingStats = true;
        protected bool ProfilingHistogram = false;
        protected string ProfilingInfo = "";
        
        #if WINDOWS
        protected List<System.Diagnostics.PerformanceCounter> counters = new List<System.Diagnostics.PerformanceCounter>();
        #endif
        #endregion

        #region Initialization
        public static void Register(Game game, ref ProfilerParameters parameters)
        {
            if (parameters.enabled)
            {
                Vector2 basePos = Vector2.Zero;
                basePos.X = parameters.X;
                basePos.Y = parameters.Y;
                instance = new ProfilerComponent(basePos, game);

                instance.MemoryStats = parameters.MemoryStats;
                instance.Performance = parameters.Performance;
                instance.Profiling = parameters.Profiling;
                instance.ProfilingStats = parameters.ProfilingStats;
                instance.ProfilingHistogram = parameters.ProfilingHistogram;
                instance.ResetTime = 1.0f / parameters.ProfilingFrequency;

                foreach (ProfilerParameters.CounterDescriptor desc in parameters.PerformanceCounters)
                {
                    System.Diagnostics.PerformanceCounter counter = null;
                    if (desc.Instance != null)
                    {
                        counter = new System.Diagnostics.PerformanceCounter(desc.Group, desc.Name, desc.Instance, true);
                    }
                    else
                    {
                        counter = new System.Diagnostics.PerformanceCounter(desc.Group, desc.Name, true);
                    }
                    instance.counters.Add(counter);
                }

                game.Components.Add(instance);
            }
        }
        
        protected ProfilerComponent(Vector2 basePos, Game game)
            : base(basePos, game)
        {
        }
        #endregion

        #region Update
        protected void UpdateProfilingInfo()
        {
            #if PROFILER
            StringBuilder builder = new StringBuilder();

            if (MemoryStats)
            {

                builder.Append("--------------- Memory: ---------------\n");
                builder.AppendFormat("Garbage collections (GEN 0): {0}\n", System.GC.CollectionCount(0));
                builder.AppendFormat("Garbage collections (GEN 1): {0}\n", System.GC.CollectionCount(1));
                builder.AppendFormat("Garbage collections (GEN 2): {0}\n", System.GC.CollectionCount(2));
                builder.AppendFormat("Managed memory used: {0}\n", System.GC.GetTotalMemory(false));
            }

            #if WINDOWS
            if (Performance)
            {
                builder.Append("--------------- Performance Counters: ---------------\n");
                foreach (System.Diagnostics.PerformanceCounter counter in counters)
                {
                    builder.AppendFormat("{0}: {1}\n", counter.CounterName, counter.NextValue());
                }
            }
            #endif

            if (Profiling)
            {
                builder.Append(Profile.Dump(ProfilingStats, ProfilingHistogram));
            }

            ProfilingInfo = builder.ToString();
            #else
            ProfilingInfo = "You must compile the game with PROFILER to allow profiling";
            #endif
        }

        protected override void OnUpdate(GameTime gameTime)
        {
            #if PROFILER
            elapsedTime += gameTime.ElapsedGameTime;
            if (elapsedTime > TimeSpan.FromSeconds(ResetTime))
            {
                elapsedTime -= TimeSpan.FromSeconds(ResetTime);
                UpdateProfilingInfo();
                Profile.ResetAll();
            }
            #endif
        }
        #endregion

        #region Draw
        protected override void OnDraw2D(GameTime gameTime)
        {
            #if PROFILER
            Color color = Color.White;
            DrawString(0, 0, ProfilingInfo, ref color);
            #else
            Color color = Color.Red;
            DrawString(0, 0, ProfilingInfo, ref color);
            #endif
        }

        protected override void OnDraw3D(GameTime gameTime)
        {
        }
        #endregion
    }
}
